﻿#if NETSTANDARD2_1_OR_GREATER
using Microsoft.AspNetCore.Http;
#elif NET451_OR_GREATER
using System.Web;
#endif
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using T.FAS.Runtime.Action.SPI;
using T.ServiceComponent.Cluster.Client;
using T.ServiceComponent.Config.Client;
using T.ServiceComponent.Kernel.Service;
using AppContext = T.ServiceComponent.Session.Service.AppContext;

namespace T.FAS.Runtime.Action.Engine
{
    /// <summary>
    /// SG服务执行代理，与具体的SG站点打交道
    /// </summary>
    internal class SGInvoker
    {
        HttpInvokeMode invokeMode = HttpInvokeMode.DefaultDomain;
        const string DDPDomainUrl = "FAS.Action.SGQueryDomain";
        string TargetDomainConfigKey = "";
        public SGInvoker(HttpInvokeMode mode, string domainConfigKey)
        {
            invokeMode = mode;
            TargetDomainConfigKey = domainConfigKey;
        }
        public SGInvoker()
        {
            invokeMode = HttpInvokeMode.DefaultDomain;
        }
        /// <summary>
        /// 向指定的SG站点发起Post调用，超时时间默认10分钟
        /// </summary>
        /// <param name="dataCenterId"></param>
        /// <param name="serviceId"></param>
        /// <param name="parameter"></param>
        /// <param name="timeoutMinitues"></param>
        /// <returns></returns>
        internal AppDebugResponse Post(string serviceId, Dictionary<string, object> parameter, Dictionary<string, string> cookies, int timeoutMinitues = 10)
        {
            ServicePointManager.DefaultConnectionLimit = 2048;
            AppDebugResponse response = new AppDebugResponse();
            var domain = GetDataCenterDomain();

            try
            {
                var request = (HttpWebRequest)WebRequest.Create(domain + "/api/invoke?SID=" + serviceId);
                request.Timeout = timeoutMinitues * 600 * 1000;//默认超时时间10分钟
                if (cookies?.Keys.Count > 0 && Uri.TryCreate(domain, UriKind.Absolute, out Uri uri))
                {
                    request.CookieContainer = new CookieContainer();
                    foreach (var item in cookies.Keys)
                    {
                        request.CookieContainer.Add(new Cookie(item, WebUtility.UrlEncode(cookies[item]), "/", uri.Host));
                    }
                }
                request.Headers.Add("SGC-SData", AppContext.Current.SessionID);
                //从线程上下文中获取SGC-TokenData，用于充电架构升级后还原Session
                var token = ContextHelper.Get("T-Token-Value");
                if (string.IsNullOrEmpty(token) == false)
                {
                    request.Headers.Add("SGC-TokenData", token);
                }
                //暂时屏蔽SGC-SessionData逻辑 提升性能
                //if (AppContext.Current.Session != null)
                //{
                //    try
                //    {
                //        string sessionStr = GetSessionString();
                //        string base64Session = Base64Encrypt(sessionStr);
                //        request.Headers.Add("SGC-SessionData", base64Session);
                //    }
                //    catch (Exception e)
                //    {
                //    }
                //}
                request.Headers["Accept-Language"] = AppContext.Current.Language?.ID;
                if (parameter == null)
                    parameter = new Dictionary<string, object>();
                if (parameter.ContainsKey(ActionRTPConst.DevAppIDKey))
                {
                    request.Headers.Add(ActionRTPConst.DevAppIDKey, Convert.ToString(parameter[ActionRTPConst.DevAppIDKey]));
                }
                else
                {
                    //var devappid = GetDevAppId();
                    //if (!string.IsNullOrEmpty(devappid))
                    //{
                    //    request.Headers.Add(ActionRTPConst.DevAppIDKey, devappid);
                    //}
                }

                var newdict = new Dictionary<string, object>();
                foreach (KeyValuePair<string, object> kv in parameter)
                {
                    if (kv.Value != null && kv.Value.GetType() != typeof(string))
                    {
                        newdict[kv.Key] = JsonConvert.SerializeObject(kv.Value);
                    }
                    else
                    {
                        newdict[kv.Key] = kv.Value;
                    }
                }

                var parameterJson = JsonConvert.SerializeObject(newdict);
                request.Method = "POST";
                request.ContentType = "application/json";

                using (var streamWriter = new StreamWriter(request.GetRequestStream()))
                {
                    streamWriter.Write(parameterJson);
                    streamWriter.Flush();
                    streamWriter.Close();
                }

                using (var httpResponse = (HttpWebResponse)request.GetResponse())
                {
                    using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
                    {
                        var result = streamReader.ReadToEnd();
                        if (httpResponse != null) httpResponse.Close();
                        object returnState = "1";
                        object returnErrorMessage = "";
                        try
                        {
                            var returnResult = JsonConvert.DeserializeObject<Dictionary<string, object>>(result);

                            if (returnResult == null)
                            {
                                returnState = "0";
                                returnErrorMessage = $"Serivce:{serviceId} Invoke Return Empty";
                            }
                            else
                            {
                                if (!returnResult.TryGetValue("state", out returnState))
                                {
                                    returnState = "1";
                                }
                                if (Convert.ToString(returnState).Equals("0"))
                                {
                                    if (!returnResult.TryGetValue("errmsg", out returnErrorMessage))
                                    {
                                        returnErrorMessage = $"Service:{serviceId} Invoke Error But Without ErrorMsg";
                                    }
                                }
                            }
                        }
                        catch
                        {
                        }
                        response.state = Convert.ToString(returnState);
                        response.data = result;
                        response.errmsg = Convert.ToString(returnErrorMessage);
                    }
                }
            }

            catch (Exception e)
            {
                response.state = "0";
                response.errmsg = $"访问站点{domain}的SG服务{serviceId}发生异常，{WrapResponseException(e)}";
            }

            return response;
        }
        // 检查非ASCII字符
        public static bool ContainsNonAscii(string value)
        {
            return value.Any(c => c > 127);
        }
        //检查保留字符（如 ; = ,）
        public static bool ContainsReservedChars(string value)
        {
            char[] reservedChars = { ';', ',', '=', ' ' };
            return value.IndexOfAny(reservedChars) >= 0;
        }
        private string WrapResponseException(Exception e)
        {
            var srvExceptionMessage = string.Empty;
            if (e is WebException)
            {
                #region WebException异常
                var webException = e as WebException;

                if (webException.Response != null)
                {
                    #region 服务端有响应返回
                    //Protocal Error
                    var httpWebResponse = webException.Response as HttpWebResponse;

                    //获取服务端具体的异常信息
                    using (var streamReader = new StreamReader(httpWebResponse.GetResponseStream()))
                    {
                        srvExceptionMessage = streamReader.ReadToEnd();
                    }

                    var responseStatusCode = (int)httpWebResponse.StatusCode;
                    return $"服务端异常编号：{responseStatusCode}，异常信息：{srvExceptionMessage}";
                    #endregion
                }
                else
                {
                    #region Response=null的服务端无响应返回，操作超时异常在这里
                    var clientStatusCode = (int)webException.Status > 9 ? Convert.ToString((int)webException.Status) : "0" + Convert.ToString((int)webException.Status);
                    return $"请求异常编号：{clientStatusCode}，异常信息：{GetExceptionStackInfo(e)}";
                    #endregion
                }
                #endregion
            }
            else
            {
                #region 非WebException异常
                return $"异常信息：{GetExceptionStackInfo(e)}";
                #endregion
            }
        }


        /// <summary>
        /// 获取SG站点地址
        /// </summary>
        /// <returns></returns>
        private string GetDataCenterDomain()
        {
            var domain = "";
            var domainKey = "";
            try
            {
                if (string.IsNullOrEmpty(TargetDomainConfigKey))
                {
                    domainKey = DDPDomainUrl;
                }
                else
                    domainKey = TargetDomainConfigKey;
                domain = ConfigManager.GetConfigValue(domainKey);
                if (string.IsNullOrEmpty(domain))
                {
                    var dataCenterId = AppClusterLocalClient.GetInstance().GetCurrentDataCenterId();
                    domain = AppClusterLocalClient.GetInstance().GetDataCenterDomain(dataCenterId, true);
                }
            }
            catch
            {
                domain = System.Configuration.ConfigurationManager.AppSettings.Get(domainKey);
            }

            if (string.IsNullOrWhiteSpace(domain))
            {
                domain = System.Configuration.ConfigurationManager.AppSettings.Get(domainKey);
                //if (string.IsNullOrWhiteSpace(domain))
                    //throw new Exception($"获取目标访问{domain}的Nginx信息为空。");
            }

            return "http://" + domain;
        }

        /// <summary>
        /// 获取完整异常堆栈信息
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        private string GetExceptionStackInfo(Exception ex)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Exception Stack:");
            sb.AppendLine(ex.StackTrace);
            var inner = ex.InnerException;
            while (inner != null)
            {
                sb.AppendLine("InnerException Message:" + inner.Message);
                sb.AppendLine("InnerException Stack:" + inner.StackTrace);
                inner = inner.InnerException;
            }
            return sb.ToString();
        }

        private string GetNgAddressByClusterService(string datacenterId)
        {
            var url = AppClusterLocalClient.GetInstance().GetDataCenterDomain(datacenterId, true);
            return url;
        }

        private static string GetSessionString()
        {
            string sessionKeysCfg = ConfigManager.GetConfigValue("TTP.Core.SessionKeys");
            if (string.IsNullOrEmpty(sessionKeysCfg))
            {
                sessionKeysCfg = "UserId,CustomerID,TAppID,SPDeviceID";
            }

            var keys = sessionKeysCfg.Split(new char[] { ',' }).ToList();

            if (AppContext.Current.Session != null)
            {
                var userID = Convert.ToString(AppContext.Current.Session["UserId"]);
            }

            return AppContext.Current.Session.ToString(keys);
        }


        private static string Base64Encrypt(string str)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(str);
            return Convert.ToBase64String(bytes);
        }

    }
}
